=begin pod

=TITLE class Attribute

=SUBTITLE Member variable

    class Attribute { }

In Perl 6 lingo, an I<attribute> refers to a per-instance/object storage slot.
An C<Attribute> is used to talk about classes' and roles' attributes on the
meta level.

Normal usage of attributes does not require the user to use class Attribute
explicitly.

The usual way to obtain an object of type C<Attribute> is by introspection:

    class Useless {
        has @!things;
    }
    my $a = Useless.^attributes(:local)[0];
    say $a.name;            # OUTPUT: «@!things␤»
    say $a.package;         # OUTPUT: «(Useless)␤»
    say $a.has_accessor;    # OUTPUT: «False␤»

    # modifying an attribute from the outside
    # this is usually not possible, but since Attribute
    # is at the level of the meta class, all is fair game
    my $instance = Useless.new;
    $a.set_value($instance, [1, 2, 3]);
    say $a.get_value($instance);        # OUTPUT: «[1 2 3]␤»

=head1 Traits

=head2 X<Trait is default|trait,is default (Attribute)>

An attribute that is assigned L<Nil|/type/Nil> will revert to its default value
set with the trait C<is default>.

    class C {
        has $.a is default(42) is rw = 666
    }
    my $c = C.new;
    say $c;
    $c.a = Nil;
    say $c;
    # OUTPUT: «C.new(a => 666)␤C.new(a => 42)␤»

=head2 X<Trait is required|trait,is required (Attribute)>

The trait C<is required> will mark the attribute as to be filled with a value
when the object is instantiated. Failing to do so will result in a runtime error.

    class C {
        has $.a is required
    }
    my $c = C.new;
    CATCH{ default { say .^name, ': ', .Str } }
    # OUTPUT: «X::Attribute::Required: The attribute '$!a' is required, but you did not provide a value for it.␤»

=head2 X<trait is DEPRECATED|trait,is DEPRECATED (Attribute)>

    multi sub trait_mod:<is>(Attribute:D $r, :$DEPRECATED!)

Marks an attribute as deprecated, optionally with a message what to use instead.

TODO: Add example when it's implemented in Rakudo.

=head1 Methods

=head2 method name

Defined as:

    method name(Attribute:D: --> Str:D)

Returns the name of the attribute.  Note that this is always the private name,
so if an attribute is declared as C<has $.a>, the name returned is C<$!a>.

    class Foo {
        has @!bar;
    }
    my $a = Foo.^attributes(:local)[0];
    say $a.name;            # OUTPUT: «@!bar␤»

=head2 method package

Defined as:

    method package(Attribute:D: --> Mu:U)

Returns the package (class/grammar/role) to which this attribute belongs.

    class Boo {
        has @!baz;
    }
    my $a = Boo.^attributes(:local)[0];
    say $a.package;         # OUTPUT: «(Boo)␤»

=head2 method has_accessor

Defined as:

    method has_accessor(Attribute:D: --> Bool:D)

Returns C<True> if the attribute has a public accessor method.

    class Container {
        has $!private;
        has $.public;
    }
    my $private = Container.^attributes(:local)[0];
    my $public = Container.^attributes(:local)[1];
    say $private.has_accessor; # OUTPUT: «False␤»
    say $public.has_accessor;  # OUTPUT: «True␤»

=head2 method readonly

Defined as:

    method readonly(Attribute:D: --> Bool:D)

Returns C<True> for readonly attributes, which is the default.
Returns C<False> for attributes marked as C<is rw>.

    class Library {
        has $.address; # Read-only value
        has @.new-books is rw;
    }
    my $addr = Library.^attributes(:local)[0];
    my $new-books = Library.^attributes(:local)[1];
    say $addr.readonly;      # OUTPUT: «True␤»
    say $new-books.readonly; # OUTPUT: «False␤»

=head2 method type

Defined as:

    method type(Attribute:D: --> Mu)

Returns the type constraint of the attribute.

    class TypeHouse {
        has Int @.array;
        has $!scalar;
        has @.mystery;
    }
    my @types = TypeHouse.^attributes(:local)[0..2];
    for 0..2 { say @types[$_].type }
    # OUTPUT: «(Positional[Int])
    # (Mu)
    # (Positional)␤»

=head2 method get_value

Defined as:

    method get_value(Attribute:D: Mu $instance)

Returns the value stored in this attribute of object C<$instance>.

    class Violated {
        has $!private-thing = 5;
    }
    my $private = Violated.^attributes(:local)[0];
    say $private.get_value(Violated.new); # OUTPUT: «5␤»

Note that this method violates encapsulation of the object, and should be
used with care.  Here be dragons.

=head2 method set_value

Defined as:

    method set_value(Attribute:D: Mu $instance, Mu \new_val)

Binds the value C<new_val> to this attribute of object C<$instance>.

    class A {
        has $!a = 5;
        method speak() { say $!a; }
    }
    my $attr = A.^attributes(:local)[0];
    my $a = A.new;
    $a.speak; # OUTPUT: «5␤»
    $attr.set_value($a, 42);
    $a.speak; # OUTPUT: «42␤»

Note that this method violates encapsulation of the object, and should be
used with care.  Here be dragons.

=head2 trait is required

    multi sub trait_mod:<is> (Attribute $attr, :$required!)

Marks an attribute as being required.  If a value is not provided
during object construction, an exception is thrown.

    class Foo {
       has $.bar is required;
       has $.baz;
    };

    Foo.new(bar => 42); # works
    Foo.new(baz => 42);
    CATCH { default { put .^name, ': ', .Str } };
    # OUTPUT: «X::Attribute::Required: The attribute '$!bar' is required, but you did not provide a value for it.␤»

C<is required> doesn't just affect the default constructor, it checks
for the attribute at a lower level, so it will work for custom
constructors written using L<bless>.


=head2 trait is rw

    multi sub trait_mod:<is> (Attribute:D $attr, :$rw!)

Marks an attribute as read/write as opposed to the default
C<readonly>.  The default accessor for the attribute will return a
writable value.

   class Boo {
      has $.bar is rw;
      has $.baz;
   };

   my $boo = Boo.new;
   $boo.bar = 42; # works
   $boo.baz = 42;
   CATCH { default { put .^name, ': ', .Str } };
   # OUTPUT: «X::Assignment::RO: Cannot modify an immutable Any␤»

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
